#include "timer.h"
#include "FluidSparseVector.h"
#include "mathlib.h"
#include "Utility.h"
#include "DecoLogger.h"

double FluidSparseVector::msAccumTime1 = 0;
double FluidSparseVector::msAccumTime2 = 0;
double FluidSparseVector::msAccumTime3 = 0;

void SimpleHashTable::init()
{
	for (int i = 0; i < NUM_BUCKETS; ++i)
	{
		mIdx3D[i] = NULL;
		mIdx1D[i] = NULL;
	}
	mLastHitIdx1D = -1;
	mLastHitIdx3D = Index3(-1, -1, -1);
}

void SimpleHashTable::clear()
{
	for (int i = 0; i < NUM_BUCKETS; ++i)
	{
		if (mIdx3D[i])
		{
			mIdx3D[i]->clear();
			delete mIdx3D[i];
			mIdx3D[i] = NULL;
		}
		if (mIdx1D[i])
		{
			mIdx1D[i]->clear();
			delete mIdx1D[i];
			mIdx1D[i] = NULL;
		}
	}
	mLastHitIdx1D = -1;
	mLastHitIdx3D = Index3(-1, -1, -1);
}
void SimpleHashTable::copy(const SimpleHashTable& rhs)
{
	clear();
	for (int i = 0; i < NUM_BUCKETS; ++i)
	{
		if (rhs.mIdx3D[i])
		{
			mIdx3D[i] = new std::vector<Index3>;
			mIdx1D[i] = new std::vector<int>;
			*(mIdx3D[i]) = *(rhs.mIdx3D[i]);
			*(mIdx1D[i]) = *(rhs.mIdx1D[i]);
		}
	}
}
SimpleHashTable::SimpleHashTable()
{
	init();
}

SimpleHashTable::SimpleHashTable(const SimpleHashTable& rhs)
{
	init();
	copy(rhs);
}

SimpleHashTable::~SimpleHashTable()
{
	clear();
}

const SimpleHashTable& SimpleHashTable::operator= (const SimpleHashTable& rhs)
{
	if (this == &rhs)
		return *this;
	copy(rhs);
	return *this;
}

void SimpleHashTable::Reset()
{
	clear();
}
void SimpleHashTable::SetIndex(const Index3& idx, int realIdx)
{
	int hashValue = hash(idx);
	if (!mIdx3D[hashValue])
	{
		mIdx3D[hashValue] = new std::vector<Index3>;
		mIdx1D[hashValue] = new std::vector<int>;
	}
	mIdx3D[hashValue]->push_back(idx);
	mIdx1D[hashValue]->push_back(realIdx);
	mLastHitIdx3D = idx;
	mLastHitIdx1D = realIdx;
	//std::vector<Index3>::iterator it = find(mIdx3D[hashValue]->begin(), mIdx3D[hashValue]->end(), idx);

	//if (it != mIdx3D[hashValue]->end())
	//	(*mIdx1D[hashValue])[it - mIdx3D[hashValue]->begin()] = realIdx;
	//else
	//{
	//	mIdx3D[hashValue]->push_back(idx);
	//	mIdx1D[hashValue]->push_back(realIdx);
	//}
}

int SimpleHashTable::GetIndex(const Index3& idx)
{
	if (mLastHitIdx3D == idx)
		return mLastHitIdx1D;
	int hashValue = hash(idx);
	if (!mIdx3D[hashValue])
		return -1;
//	if (mIdx3D[hashValue]->size() > 20)
//		(*DecoLogger::GetSingleton()) << "num of entries in " << hashValue << "th bucket is " << static_cast<int>(mIdx3D[hashValue]->size()) << "\n";
	std::vector<Index3>::iterator it = find(mIdx3D[hashValue]->begin(), mIdx3D[hashValue]->end(), idx);

	if (it != mIdx3D[hashValue]->end())
	{
		mLastHitIdx3D = idx;
		mLastHitIdx1D = (*mIdx1D[hashValue])[it - mIdx3D[hashValue]->begin()]; 
		return mLastHitIdx1D;
	}
	else
		return -1;

}

int SimpleHashTable::hash(const Index3& idx)
{
	return (idx.m_i * 997 + idx.m_j * 449 + idx.m_k) % NUM_BUCKETS;
}


FluidSparseVector::FluidSparseVector(int numX, int numY, int numZ, int dim) : mIndexTable(NULL)
{
	SetDimension(numX, numY, numZ, dim);
}
FluidSparseVector::FluidSparseVector()
{
	mNumX = mNumY = mNumZ = mDim = 0;
	mIndexTable = NULL;
}

FluidSparseVector::FluidSparseVector(const FluidSparseVector& rhs) : mNumX(rhs.mNumX), mNumY(rhs.mNumY), mNumZ(rhs.mNumZ), mDim(rhs.mDim), mIndexTable(NULL)
{
	mValues = rhs.mValues;
	mIndices = rhs.mIndices;
	//mIndexTable = Malloc3DArray<int>(mNumX, mNumY, mNumZ);
	//CopyTo3DArray(mIndexTable, rhs.mIndexTable, mNumX, mNumY, mNumZ);
}

const FluidSparseVector& FluidSparseVector::operator= (const FluidSparseVector& rhs)
{
	if (this == &rhs)
		return *this;
	//if (mIndexTable)
	//	Free3DArray(mIndexTable, mNumX, mNumY, mNumZ);

	mNumX = rhs.mNumX;
	mNumY = rhs.mNumY;
	mNumZ = rhs.mNumZ;
	mDim = rhs.mDim;
	mValues = rhs.mValues;
	mIndices = rhs.mIndices;

	//mIndexTable = Malloc3DArray<int>(mNumX, mNumY, mNumZ);
	//CopyTo3DArray(mIndexTable, rhs.mIndexTable, mNumX, mNumY, mNumZ);
	return *this;
}

int FluidSparseVector::GetDim() const
{
	return mDim;
}


FluidSparseVector::~FluidSparseVector()
{
	if (mIndexTable)
		delete mIndexTable;

	mValues.clear();
	mIndices.clear();
	mNumX = mNumY = mNumZ = 0;
}

void FluidSparseVector::SetZero()
{
	mValues.clear();
	mIndices.clear();
	if (mIndexTable)
		mIndexTable->Reset();
}

void FluidSparseVector::SetDimension(int numX, int numY, int numZ, int dim)
{
	mNumX = numX;
	mNumY = numY;
	mNumZ = numZ;
	mDim = dim;
}

const FluidSparseVector& FluidSparseVector::operator+= (const FluidSparseVector& rhs)
{
	int len = static_cast<int>(rhs.mIndices.size());
	for (int it = 0; it < len; ++it)
	{
		int i = rhs.mIndices[it].m_i;
		int j = rhs.mIndices[it].m_j;
		int k = rhs.mIndices[it].m_k;
		SetValue(i, j, k, GetValue(i, j, k) + rhs.mValues[it]);
	}
	return *this;
}

const FluidSparseVector& FluidSparseVector::operator-= (const FluidSparseVector& rhs)
{
	int len = static_cast<int>(rhs.mIndices.size());
	for (int it = 0; it < len; ++it)
	{
		int i = rhs.mIndices[it].m_i;
		int j = rhs.mIndices[it].m_j;
		int k = rhs.mIndices[it].m_k;
		SetValue(i, j, k, GetValue(i, j, k) - rhs.mValues[it]);
	}
	return *this;
}

const FluidSparseVector& FluidSparseVector::operator*= (double alpha)
{
	for (std::vector<double>::iterator it = mValues.begin(); it != mValues.end(); ++it)
	{
		*it *= alpha;
	}
	return *this;
}
const FluidSparseVector& FluidSparseVector::operator/= (double alpha)
{
	for (std::vector<double>::iterator it = mValues.begin(); it != mValues.end(); ++it)
	{
		*it /= alpha;
	}
	return *this;
}


void FluidSparseVector::SetValue(int i, int j, int k, double value)
{
//	DecoTimer timer;
//	timer.startTimer();
	int idx1D = -1;
//	timer.resetTimer();
	if (mIndexTable)
	{

		idx1D = mIndexTable->GetIndex(Index3(i, j, k));
	}
	else
	{
		vector<Index3>::iterator it = find(mIndices.begin(), mIndices.end(), Index3(i, j, k));
		if (it != mIndices.end())
			idx1D = it - mIndices.begin();
	}
//	msAccumTime1 += timer.getCurrentTime();
//	timer.resetTimer();
	if (idx1D == -1)
	{
		if (abs(value) < 1e-9)
			return;
		mIndices.push_back(Index3(i, j, k));
		mValues.push_back(value);


		if (mIndexTable)
			mIndexTable->SetIndex(Index3(i,j,k), static_cast<int>(mValues.size()) - 1);

	}
	else
	{
		if (0)//abs(value) < 1e-9)
		{
			mIndices.erase(mIndices.begin() + idx1D);
			mValues.erase(mValues.begin() + idx1D);
			if (mIndexTable)
			{
				mIndexTable->Reset();
				for (size_t i = 0; i < mIndices.size(); ++i)
				{
					const Index3& idx = mIndices[i];
					mIndexTable->SetIndex(idx, static_cast<int>(i));
				}

			}
		}
		else
		{
			mValues[idx1D] = value;
		}
	}
//	msAccumTime2 += timer.getCurrentTime();
//	timer.resetTimer();
	if (mIndices.size() > 10 && !mIndexTable)
	{
		mIndexTable = new SimpleHashTable();
		mIndexTable->Reset();
		for (size_t i = 0; i < mIndices.size(); ++i)
		{
			const Index3& idx = mIndices[i];
			mIndexTable->SetIndex(idx, static_cast<int>(i));
		}

	}
//	msAccumTime3 += timer.getCurrentTime();
	//vector<Index3>::iterator it = find(mIndices.begin(), mIndices.end(), Index3(i, j, k));
	//if (it == mIndices.end())
	//{
	//	if (abs(value) < 1e-9)//EPSILON_FLOAT)
	//		return;
	//	mIndices.push_back(Index3(i, j, k));
	//	mValues.push_back(value);
	//}
	//else
	//{
	//	int pos = it - mIndices.begin();

	//	if (abs(value) < 1e-9)//EPSILON_FLOAT) 
	//	{
	//		mIndices.erase(it);
	//		mValues.erase(mValues.begin() + pos);
	//	}
	//	else
	//	{
	//		mValues[pos] = value;

	//	}

	//}

}

void FluidSparseVector::AddValue(int i, int j, int k, double value)
{
	int idx1D = -1;
	if (mIndexTable)
		idx1D = mIndexTable->GetIndex(Index3(i, j, k));
	else
	{
		vector<Index3>::iterator it = find(mIndices.begin(), mIndices.end(), Index3(i, j, k));
		if (it != mIndices.end())
			idx1D = it - mIndices.begin();
	}
	if (idx1D != -1)		
		mValues[idx1D] += value;
	else
	{
		if (abs(value) < 1e-9)
			return;
		mIndices.push_back(Index3(i, j, k));
		mValues.push_back(value);


		if (mIndexTable)
			mIndexTable->SetIndex(Index3(i,j,k), static_cast<int>(mValues.size()) - 1);

		if (mIndices.size() > 10 && !mIndexTable)
		{
			mIndexTable = new SimpleHashTable();
			mIndexTable->Reset();
			for (size_t i = 0; i < mIndices.size(); ++i)
			{
				const Index3& idx = mIndices[i];
				mIndexTable->SetIndex(idx, static_cast<int>(i));
			}
		}
	}

	return;
}

double FluidSparseVector::GetValue(int i, int j, int k) 
{
	int idx1D = -1;
	if (mIndexTable)
		idx1D = mIndexTable->GetIndex(Index3(i, j, k));
	else
	{
		vector<Index3>::iterator it = find(mIndices.begin(), mIndices.end(), Index3(i, j, k));
		if (it != mIndices.end())
			idx1D = it - mIndices.begin();
	}
	if (idx1D != -1)		
		return mValues[idx1D];
	else
		return 0.0;
	//vector<Index3>::const_iterator it = find(mIndices.begin(), mIndices.end(), Index3(i, j, k));
	//if (it == mIndices.end())
	//	return 0.0;
	//int pos = it - mIndices.begin();
	//return mValues[pos];
}

FluidSparseVector operator+(const FluidSparseVector& a, const FluidSparseVector& b)
{
	FluidSparseVector ret(a);
	ret += b;
	return ret;
}

FluidSparseVector operator-(const FluidSparseVector& a, const FluidSparseVector& b)
{
	FluidSparseVector ret(a);
	ret -= b;
	return ret;
}

FluidSparseVector operator -(const FluidSparseVector &rhs)
{
	FluidSparseVector ret(rhs);
	for (vector<double>::iterator it = ret.mValues.begin(); it != ret.mValues.end(); ++it)
	{
		*it = -(*it);
	}
	return ret;
}


FluidSparseVector operator* (double alpha, const FluidSparseVector& rhs)
{
	FluidSparseVector ret(rhs);
	ret *= alpha;
	return ret;
}
FluidSparseVector operator/ (double alpha, const FluidSparseVector& rhs)
{
	FluidSparseVector ret(rhs);
	ret /= alpha;
	return ret;
}